from functools import partial
from pathlib import Path

import pytest

TOWNCRIER_CHANGELOG_TPL = """\
# CHANGELOG - dummy

<!-- towncrier release notes start -->

{}
"""

MANUAL_CHANGELOG_TPL = """\
# CHANGELOG - dummy

{}
"""

EXAMPLE_RELEASE = """\
## {} / 2024-07-15

***Added***:

* Initial Release
"""

ABOUT_PY_TPL = """\
# (C) Datadog, Inc. 2022-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
__version__ = '{}'
"""


def write_file(fake_repo, fpath, content):
    full_path = fake_repo.path / fpath

    full_path.parent.mkdir(exist_ok=True, parents=True)
    full_path.write_text(content)
    return full_path


@pytest.fixture
def validate_version(ddev):
    return partial(ddev, 'validate', 'version')


@pytest.mark.parametrize(
    'repo_fixture',
    [pytest.param('fake_extras_repo', id='extras'), pytest.param('fake_marketplace_repo', id='marketplace')],
)
def test_not_integrations_core(request, repo_fixture, validate_version):
    _repo = request.getfixturevalue(repo_fixture)
    result = validate_version('dummy')
    assert result.exit_code == 1, result.output
    assert 'Version validation is only available for repo `core`,' in result.output


@pytest.mark.parametrize(
    'about_py_tpl',
    [
        pytest.param(ABOUT_PY_TPL, id='single quotes around version in __about__.py'),
        pytest.param(ABOUT_PY_TPL.replace("'", '"'), id='double quotes around version in __about__.py'),
    ],
)
def test_versions_match(about_py_tpl, fake_repo, validate_version):
    """
    Given a Python package with an autogenerated CHANGELOG where versions match, the validation should pass.
    """

    v = '1.2.0'
    write_file(fake_repo, 'dummy/CHANGELOG.md', TOWNCRIER_CHANGELOG_TPL.format(EXAMPLE_RELEASE.format(v)))
    write_file(fake_repo, 'dummy/datadog_checks/dummy/__about__.py', about_py_tpl.format(v))

    result = validate_version('dummy')

    assert result.exit_code == 0, result.output
    assert 'Version checks out.' in result.output


def test_versions_dont_match(fake_repo, validate_version, helpers):
    """
    Given a Python package with an autogenerated CHANGELOG where versions don't match, the validation should fail.
    """
    v1 = '1.1.0'
    v2 = '1.2.0'
    changelog = write_file(fake_repo, 'dummy/CHANGELOG.md', TOWNCRIER_CHANGELOG_TPL.format(EXAMPLE_RELEASE.format(v1)))
    about_py = write_file(fake_repo, 'dummy/datadog_checks/dummy/__about__.py', ABOUT_PY_TPL.format(v2))

    result = validate_version('dummy')

    assert result.exit_code == 1, result.output
    assert f'Version {v2} from {about_py.relative_to(fake_repo.path)} does not match ' in result.output
    assert f'{v1} which is the latest version from {changelog.relative_to(fake_repo.path)}' in result.output


def test_autogenerated_changelog_requires_python_package(fake_repo, validate_version):
    """
    If the CHANGELOG is autogenerated we should have a Python package (with __about__.py).

    We fail if the package is missing.
    """
    v = '1.2.0'
    changelog = write_file(fake_repo, 'dummy/CHANGELOG.md', TOWNCRIER_CHANGELOG_TPL.format(EXAMPLE_RELEASE.format(v)))

    result = validate_version('dummy')

    assert result.exit_code == 1, result.output
    assert f'{changelog.relative_to(fake_repo.path)} expects a Python package' in result.output, result.output
    assert f'missing {Path("dummy/datadog_checks/dummy/__about__.py")}.' in result.output, result.output


def test_python_package_changelog_missing(fake_repo, validate_version):
    """
    An integration that's a Python package needs the CHANGELOG to be present.
    """
    v = '1.2.0'
    write_file(fake_repo, 'dummy/datadog_checks/dummy/__about__.py', ABOUT_PY_TPL.format(v))

    result = validate_version('dummy')

    assert result.exit_code == 1, result.output
    assert f'This looks like a Python package, but {Path("dummy/CHANGELOG.md")} is missing.' in result.output


def test_python_package_manual_changelog(fake_repo, validate_version):
    """
    An integration that's a Python package needs the CHANGELOG to be generated automatically.
    """
    v = '1.2.0'
    changelog = write_file(fake_repo, 'dummy/CHANGELOG.md', MANUAL_CHANGELOG_TPL.format(EXAMPLE_RELEASE.format(v)))
    write_file(fake_repo, 'dummy/datadog_checks/dummy/__about__.py', ABOUT_PY_TPL.format(v))

    result = validate_version('dummy')

    assert result.exit_code == 1, result.output
    assert f'This looks like a Python package, but {changelog.relative_to(fake_repo.path)} is managed' in result.output
    assert 'Please add the towncrier header to the CHANGELOG.' in result.output


def test_new_python_package_integration(fake_repo, validate_version):
    """
    When we add a new python package the CHANGELOG should be autogenerated and empty, __about__.py should be v0.0.1
    """
    v = '0.0.1'
    write_file(fake_repo, 'dummy/CHANGELOG.md', TOWNCRIER_CHANGELOG_TPL.format(''))
    write_file(fake_repo, 'dummy/datadog_checks/dummy/__about__.py', ABOUT_PY_TPL.format(v))

    result = validate_version('dummy')

    assert result.exit_code == 0, result.output
    assert 'Version checks out.' in result.output


def test_new_python_package_integration_changelog_has_release(fake_repo, validate_version):
    """
    When we add a new python package the CHANGELOG should be autogenerated and empty.
    """
    v = '0.0.1'
    changelog = write_file(fake_repo, 'dummy/CHANGELOG.md', TOWNCRIER_CHANGELOG_TPL.format(EXAMPLE_RELEASE.format(v)))
    about_py = write_file(fake_repo, 'dummy/datadog_checks/dummy/__about__.py', ABOUT_PY_TPL.format(v))

    result = validate_version('dummy')

    assert result.exit_code == 1, result.output
    assert f'The version {v} from {about_py.relative_to(fake_repo.path)}' in result.output
    assert f'{changelog.relative_to(fake_repo.path)} should not contain' in result.output


def test_released_python_package_with_empty_changelog(fake_repo, validate_version):
    """
    We can't have the package version be >=1.0.0 (implying we released it) and an empty CHANGELOG.
    """
    v = '1.0.0'
    write_file(fake_repo, 'dummy/CHANGELOG.md', TOWNCRIER_CHANGELOG_TPL.format(''))
    about_py = write_file(fake_repo, 'dummy/datadog_checks/dummy/__about__.py', ABOUT_PY_TPL.format(v))

    result = validate_version('dummy')

    assert result.exit_code == 1, result.output
    assert 'Getting conflicting information.' in result.output
    assert f'Version {v} from' in result.output
    assert f'{about_py.relative_to(fake_repo.path)}' in result.output
    assert 'Either we need to add a release to the CHANGELOG or roll back the version.' in result.output


def test_tile_only_integration(fake_repo, validate_version):
    """
    There should be no about.py and the version should not be missing.
    """
    v = '2.0.0'
    write_file(fake_repo, 'dummy/CHANGELOG.md', MANUAL_CHANGELOG_TPL.format(EXAMPLE_RELEASE.format(v)))

    result = validate_version('dummy')

    assert result.exit_code == 0, result.output
    assert 'Version checks out.' in result.output


def test_tile_only_integration_missing_version(fake_repo, validate_version):
    """
    The CHANGELOG should contain at least one release.
    """
    write_file(fake_repo, 'dummy/CHANGELOG.md', MANUAL_CHANGELOG_TPL.format(''))

    result = validate_version('dummy')

    assert result.exit_code == 1, result.output
    assert f'Missing a version in {Path("dummy/CHANGELOG.md")}, please add one.' in result.output
